#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sched.h>
#include <signal.h>
#include <assert.h>

#include <algorithm>

#include "bcti_Thread.h"
#include "bcti_ScopedMutex.h"

#include "easyLog.h"

#define THREAD_POLICY SCHED_RR

namespace lzpLib{

Mutex Thread::threadsMutex_;
std::vector<Thread*> Thread::threads_;

Thread::Thread(Runnable &target, bool deleteSelf, const std::string &name, int priority, int stackSize)
        :target_(target), deleteSelf_(deleteSelf), name_(name), priority_(priority), stackSize_(stackSize), state_(NEW){
	INFO_("Thread::Thread [%p]\n",this);
    threads_.push_back(this);
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
    pthread_mutex_init(&stateMutex_, &attr);
    pthread_mutex_init(&stateStartMutex_, &attr);
    pthread_cond_init(&stateCond_, NULL);
}

Thread::~Thread(){
	INFO_("~Thread::Thread [%p]\n",this);
    threadsMutex_.lock();
    std::vector<Thread*>::iterator it = find(threads_.begin(), threads_.end(), this);
    if (it != threads_.end()) {
        threads_.erase(it);
    }
    threadsMutex_.unlock();
    wait(0);
}

static void maskSignals(void){
    sigset_t maskedSignals;
    sigemptyset(&maskedSignals);
    sigaddset(&maskedSignals, SIGTERM);
    sigprocmask(SIG_BLOCK, &maskedSignals, NULL);
}

void Thread::localstart(void){
    pthread_mutex_lock(&stateStartMutex_);
    pthread_mutex_unlock(&stateStartMutex_);
    maskSignals();
    try{        
        target_.run();
    }
    catch (std::exception &e)
    {
        ERR_("exception in create thread %s n message: %s\n", name_.c_str(), e.what());
    }
    catch (...)
    {
        ERR_("unknown exception in create thread %s\n", name_.c_str());
    }

    pthread_mutex_lock(&stateMutex_);
    state_ = TERMINATED;
    pthread_cond_broadcast(&stateCond_);
    pthread_mutex_unlock(&stateMutex_);

    if (deleteSelf_) {
        delete this;
    }
}

void* Thread::localstart(void* p){
    Thread* thread = static_cast<Thread*>(p);
    thread->localstart();
    return 0;
}

void Thread::start(void){
    pthread_mutex_lock(&stateMutex_);
    
    assert(state_ != RUNNING);
    if ( state_ == RUNNING) {
        pthread_mutex_unlock(&stateMutex_);
        return;
    }
    state_ = RUNNING;

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    if(deleteSelf_) {
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    }
    
    pthread_attr_setstacksize(&attr, stackSize_);

    pthread_attr_setschedpolicy(&attr, THREAD_POLICY);

    struct sched_param schedParam;
    schedParam.sched_priority = priority_;
    pthread_attr_setschedparam(&attr, &schedParam);

    pthread_mutex_lock(&stateStartMutex_);
    int ret = pthread_create(&thread_, &attr, localstart, this);
    pthread_mutex_unlock(&stateStartMutex_);
    pthread_attr_destroy(&attr);

    if (ret){
        state_ = JOINED;
    }
    pthread_mutex_unlock(&stateMutex_);
}

int Thread::joinThread(){
    int err = 0;
    void* ret;
    if (thread_ != 0){
        err = pthread_join(thread_, &ret);
    }
    return err;
}

ThreadStatus Thread::setPriority(int priority){
    struct sched_param schedParam;
    schedParam.sched_priority = priority;
    int result = pthread_setschedparam(thread_, THREAD_POLICY, &schedParam);
    if(result != 0){
        return Thread_Fail;
    }
    priority_ = priority;
    return Thread_OK;
}

ThreadStatus Thread::wait(int milliseconds){
    pthread_mutex_lock(&stateMutex_);
    if ( pthread_equal(thread_, pthread_self()) ) {
        pthread_mutex_unlock(&stateMutex_);
        return Thread_WaitingSelf;
    }

    int ret = 0;
    if (state_ == RUNNING) {
        if (milliseconds > 0){         
            struct timespec timeout;
            clock_gettime(CLOCK_REALTIME, &timeout);
            timeout.tv_sec  += milliseconds / 1000;
            timeout.tv_nsec += milliseconds %1000 * 1000 * 1000;
            ret = pthread_cond_timedwait(&stateCond_, &stateMutex_,  &timeout);
        } else {
            ret = pthread_cond_wait(&stateCond_, &stateMutex_);
        }
    }

    if (state_ == TERMINATED){
        joinThread();
        state_ = JOINED;
    }

    pthread_mutex_unlock(&stateMutex_);

    if (ret == ETIMEDOUT)
    {
        return Thread_TimeOut;
    }
    else if (ret == EINTR)
    {
        return Thread_Interrupted;
    }
    return Thread_OK;
}

ThreadStatus Thread::sleep(int milliseconds){
    ThreadStatus err = Thread_OK;
    struct timespec timeout;
    timeout.tv_sec  = milliseconds / 1000;
    timeout.tv_nsec = (milliseconds % 1000) * 1000 * 1000;

    int ret = nanosleep(&timeout, NULL);
    if (ret == -1){
        if (errno == EINTR){
            err = Thread_Interrupted;
        }else{
            err = Thread_Fail;
        }
    }
    return (err);
}

ThreadStatus Thread::yield(){
    return sched_yield() ? Thread_Fail : Thread_OK;
}

Thread* Thread::current(void){
    Thread* currentThread = NULL;
    pthread_t selfThread = ::pthread_self();
    ScopedMutex lock(threadsMutex_);
    for (std::vector<Thread*>::iterator thread = threads_.begin(); thread != threads_.end(); ++thread) {
        if (*thread) {
            if (pthread_equal(selfThread, (*thread)->thread_)){
                currentThread = *thread;
                break;
            }
        }
    }
    return (currentThread);
}

}

